/***********************************************************************************
Copy right:	    Coffee Tech.
Author:         jiaoyue
Date:           2019.8.29
Description:    nv组件接口
***********************************************************************************/

#include <nv_utils.h>
#include <pthread.h>
//#include <cmd_utils.h>
#include <regex.h>
#include <file_opt.h>

static struct unix_param unix_para;
static pthread_mutex_t mutex_nv_util;
static pthread_mutex_t mutex_nv_list;
static int parse_json(cJSON *root, struct list_head *nv_head);
static struct timeval timeout;

#define OBJ_NAME_SIZE 64
static char *key_arr[OBJ_NAME_SIZE];
static char total_key[MAX_NV_NAME_LEN] = {0};
static int key_index = 0;

#define DEFAULT_TIMEOUT 3


/**
 * @brief 判断设置指令是否正确
 * @return 0 正确 -1 错误
 */
static int is_correct_setcmd(const char *cmdstr)
{
    assert(cmdstr != NULL);

    char *ptr = strstr(cmdstr, "=");
    if(ptr == NULL)
    {
        return -1;
    }

    int len = (int)(ptr - cmdstr);

    if(len == strlen(cmdstr) - 1)
    {
        return -1;
    }

    return 0;
}

/**
 * @brief 初始化参数子系统客户端组件
 * @param mod_name 子系统或模块名称
 * @return 0 -1
 */
int nv_client_init(const char *mod_name)
{
    int ret;
    timeout.tv_sec = DEFAULT_TIMEOUT;
    ret = unix_init_client(&unix_para, mod_name, SERVICE_NV_NAME);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "error unix_init_log_client");
        return -1;
    }

    pthread_mutex_init(&mutex_nv_util, NULL);
    pthread_mutex_init(&mutex_nv_list, NULL);

    return 0;
}

/**
 * @brief 获取原始json数据
 * @param name 配置名称
 * @param val 接收值缓冲区
 * @param val_len 缓冲区大小，可以直接赋值 MAX_NV_VAL_LEN
 * @return 成功返回值引用，失败NULL
 */
char *nv_get_origin(const char *name, char *val, int val_len)
{
    char send_buf[MAX_NV_VAL_LEN + 10];

    snprintf(send_buf, MAX_NV_VAL_LEN+10, "nv get %s", name);
//    LOG_STD(APP_DEBUG, "send_buf = %s", send_buf);

    pthread_mutex_lock(&mutex_nv_util); //锁定
    int ret = unix_client_send(&unix_para, send_buf, strlen(send_buf)+1);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_send nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return NULL;
    }

    timeout.tv_sec = DEFAULT_TIMEOUT;
    ret = unix_client_recv(&unix_para, val, val_len, &timeout);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_recv nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return NULL;
    }

//    LOG_STD(APP_DEBUG, "recv_buf = %s", val);
    pthread_mutex_unlock(&mutex_nv_util); //解锁
    return val;
}

/**
 * @brief 设置参数
 * @param set_cmd 设置命令
 * @return 0 -1
 */
int nv_set_obj(const char *set_cmd)
{
    char send_buf[MAX_NV_VAL_LEN + 10];
    char recv_buf[10] = {0};

    //匹配 A=B 的形式，如果不是，那么直接返回错误
    //if(!regexp(".+=.+", set_cmd))  //怕影响效率，不用正则了
    if(is_correct_setcmd(set_cmd) < 0)
    {
        LOG_STD(APP_ERR, "nv set must A=B");
        return -1;
    }

    snprintf(send_buf, MAX_NV_VAL_LEN+10, "nv set %s", set_cmd);
//    LOG_STD(APP_DEBUG, "send_buf = %s", send_buf);

    pthread_mutex_lock(&mutex_nv_util); //锁定
    int ret = unix_client_send(&unix_para, send_buf, strlen(send_buf)+1);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_send nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }

    timeout.tv_sec = DEFAULT_TIMEOUT;
    ret = unix_client_recv(&unix_para, recv_buf, 20, &timeout);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_recv nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }
    else if(ret == 0)
    {
        LOG_STD(APP_ERR, "nv set %s time out", set_cmd);
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }

    pthread_mutex_unlock(&mutex_nv_util); //解锁

//    LOG_STD(APP_DEBUG, "recv_buf = %s", recv_buf);
    if(strncmp(recv_buf, "ok", 2) == 0)
    {
        return 0;
    }
    else
    {
        LOG_STD(APP_ERR, "nv set error");
        return -1;
    }

    return 0;
}

/**
 * @brief 删除参数
 * @param del_cmd 删除命令
 * @return 0 -1
 */
int nv_del_obj(const char *del_cmd)
{
    char send_buf[MAX_NV_VAL_LEN + 10];
    char recv_buf[10] = {0};

    snprintf(send_buf, MAX_NV_VAL_LEN+10, "nv del %s", del_cmd);
//    LOG_STD(APP_DEBUG, "send_buf = %s", send_buf);

    pthread_mutex_lock(&mutex_nv_util); //锁定
    int ret = unix_client_send(&unix_para, send_buf, strlen(send_buf)+1);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_send nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }

    timeout.tv_sec = DEFAULT_TIMEOUT;
    ret = unix_client_recv(&unix_para, recv_buf, 20, &timeout);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_recv nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }
    else if(ret == 0)
    {
        LOG_STD(APP_ERR, "nv get time out");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }

    pthread_mutex_unlock(&mutex_nv_util); //解锁

//    LOG_STD(APP_DEBUG, "recv_buf = %s", recv_buf);
    if(strncmp(recv_buf, "ok", 2) == 0)
    {
        return 0;
    }
    else
    {
        LOG_STD(APP_WARNING, "nv del error");
        return -1;
    }

    return 0;
}

/**
 * @brief 获取单个参数
 * @param obj 传入参数，无需赋值
 * @param nv_name 配置名称
 * @return 失败NULL，成功返回传入的obj地址，可根据节点类型取合适的类型值使用
 */
struct nv_obj *nv_get_obj(struct nv_obj *obj, const char *nv_name)
{
    char recv_buf[MAX_NV_VAL_LEN] = {0};
    char cmd_buf[1024] = {0};
    cJSON *root, *node;

    obj->valuestring = NULL;
    strcpy(obj->nv_name, nv_name);
    snprintf(cmd_buf, 1024, "nv get %s", nv_name);
//    LOG_STD(APP_DEBUG, "cmd = %s", cmd_buf);

    pthread_mutex_lock(&mutex_nv_util); //锁定
    int ret = unix_client_send(&unix_para, cmd_buf, strlen(cmd_buf)+1);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_send nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return NULL;
    }

    memset(recv_buf, 0, MAX_NV_VAL_LEN);

    timeout.tv_sec = DEFAULT_TIMEOUT;
    ret = unix_client_recv(&unix_para, recv_buf, MAX_NV_VAL_LEN, &timeout);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_recv nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return NULL;
    }
    else if(ret == 0)
    {
        LOG_STD(APP_ERR, "nv get %s time out", nv_name);
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return NULL;
    }

    pthread_mutex_unlock(&mutex_nv_util); //解锁
    int str_len = strlen(recv_buf) + 1;  //对方给过来最后一个字符是换行，和cppjson有关
//    LOG_STD(APP_DEBUG, "origin recv_buf = %s", recv_buf);

    obj->valuestring = (char *)malloc(str_len);
    if(NULL == obj->valuestring)
    {
        LOG_STD(APP_DEBUG, "malloc err");
        return NULL;
    }

    memset(obj->valuestring, 0, str_len);
    strncpy(obj->valuestring, recv_buf, strlen(recv_buf));  //这样有没有结尾符都无所谓
//    LOG_STD(APP_DEBUG, "obj->valuestring = %s", obj->valuestring);

    //这里借助cJSON的解析功能，把数据类型解析出来并存放到相应的成员中，别忘了释放cJSON结构的内存
    char tmp_buf[MAX_NV_VAL_LEN + MAX_NV_NAME_LEN];
    sprintf(tmp_buf, "{\"%s\":%s}", obj->nv_name, obj->valuestring);
//    LOG_STD(APP_DEBUG, "tmp_buf = %s", tmp_buf);
    root = cJSON_Parse(tmp_buf);
    if (NULL == root) {
        LOG_STD(APP_ERR, "parse error, json = %s", tmp_buf);
        goto failed;
    }
    node = cJSON_GetArrayItem(root, 0);
    if(NULL == node)
    {
        LOG_STD(APP_ERR, "cJSON_GetArrayItem err");
        goto failed;
    }
//    LOG_STD(APP_DEBUG, "type = %d", node->type);

    //不管什么类型，valuestring都存放结果字符串
    char *ptmp = cJSON_Print(node);
    strcpy(obj->valuestring, ptmp);
    free(ptmp);

    obj->type = node->type;
    switch (node->type)
    {
    case cJSON_False:
    case cJSON_True:
        obj->valueint = node->valueint;
        break;
    case cJSON_Number:
        obj->valueint = node->valueint;
        obj->valuedouble = node->valuedouble;
        break;
    case cJSON_String:
        strcpy(obj->valuestring, node->valuestring);
        break;
    default:
        //cJSON_Array或者cJSON_Object类型，直接返回即可。
        break;
    }

    cJSON_Delete(root);
    return obj;

failed:
    if(obj && NULL != obj->valuestring)
    {
        free(obj->valuestring);
        obj->valuestring = NULL;
    }
    return NULL;
}

/**
 * @brief 释放单个节点内存
 * @param obj 需要释放内存的节点指针
 */
void nv_obj_free(struct nv_obj *obj)
{
    if(obj && NULL != obj->valuestring)
    {
        free(obj->valuestring);
    }
}

/**
 * @brief 获取多个参数到链表中存储
 * @param nv_head 存储链表头部
 * @param nv_name 获取节点名称，可以为父节点
 * @return 0 -1
 */
int nv_get_list(struct list_head *nv_head, const char *nv_name)
{
    //初始化链表头
    INIT_LIST_HEAD(nv_head);

    //把结果解析出来，然后创建每个节点加入到链表
    char recv_buf[MAX_NV_VAL_LEN];
    char cmd_buf[1200];
    cJSON *root;

    snprintf(cmd_buf, 1200, "nv get %s", nv_name);
//    LOG_STD(APP_DEBUG, "cmd = %s", cmd_buf);

    pthread_mutex_lock(&mutex_nv_util); //锁定
    int ret = unix_client_send(&unix_para, cmd_buf, strlen(cmd_buf)+1);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_send nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }

    timeout.tv_sec = DEFAULT_TIMEOUT;
    ret = unix_client_recv(&unix_para, recv_buf, MAX_NV_VAL_LEN, &timeout);
    if(ret < 0)
    {
        LOG_STD(APP_ERR, "unix_client_recv nv err");
        pthread_mutex_unlock(&mutex_nv_util); //解锁
        return -1;
    }

//    LOG_STD(APP_DEBUG, "recv_buf = %s", recv_buf);
    pthread_mutex_unlock(&mutex_nv_util); //解锁

    root = cJSON_Parse(recv_buf);
    if (NULL == root) {
        LOG_STD(APP_ERR, "parse error");
        return -1;
    }

    pthread_mutex_lock(&mutex_nv_list);
    memset(total_key, 0, MAX_NV_NAME_LEN);
    key_index = 0;
    if(parse_json(root, nv_head) < 0)
    {
        pthread_mutex_unlock(&mutex_nv_list);
        LOG_STD(APP_ERR, "parse_json err");
        cJSON_Delete(root);
        return -1;
    }
    pthread_mutex_unlock(&mutex_nv_list);


    cJSON_Delete(root);
    return 0;
}

/**
 * @brief 原始json字符串解析成链表
 * @param nv_head
 * @param json_str
 * @return 0 -1
 */
int nv_get_list_from_string(struct list_head *nv_head, const char *json_str)
{
    //初始化链表头
    INIT_LIST_HEAD(nv_head);

    cJSON *root;
    root = cJSON_Parse(json_str);
    if (NULL == root) {
        LOG_STD(APP_ERR, "parse error");
        return -1;
    }

    pthread_mutex_lock(&mutex_nv_list);
    memset(total_key, 0, MAX_NV_NAME_LEN);
    key_index = 0;
    if(parse_json(root, nv_head) < 0)
    {
        pthread_mutex_unlock(&mutex_nv_list);
        LOG_STD(APP_ERR, "parse_json err");
        cJSON_Delete(root);
        return -1;
    }
    pthread_mutex_unlock(&mutex_nv_list);

    cJSON_Delete(root);

    return 0;
}


/**
 * @brief 原始文件解析成链表
 * @param nv_head   链表头
 * @param file_name 文件名
 * @return 0 -1
 */
int nv_get_list_from_file(struct list_head *nv_head, const char *file_name)
{
    char *js_buf_p;
    long js_file_len;
    //判断文件是否存在
    if(fileopt_exist(file_name) == FALSE)
    {
        LOG_STD(APP_ERR, "%s NOT exist", file_name);
        return -1;
    }

    //获取文件大小
    js_file_len = fileopt_getsize(file_name);
    if(js_file_len == 0)
    {
        LOG_STD(APP_ERR, "fileopt_getsize ERR \n");
        return -1;
    }else{
        LOG_STD(APP_DEBUG, "js_file_len size is %d\n", js_file_len);
    }

    //此时开辟空间,可有效利用空间
    js_buf_p = (char *)malloc(js_file_len);
    if(js_buf_p == NULL)
    {
        LOG_STD(APP_ERR, "malloc ERR \n");
        return -1;
    }

    //读取文件
    if(file_opt_read(file_name, js_buf_p, js_file_len) == 0)
    {
        LOG_STD(APP_ERR, "Read file failed \n");
        return -1;
    }else
    {
       // LOG_STD(APP_DEBUG, "Read file seccess \n %s\n", js_buf_p);
    }

    //获取链表
    if(nv_get_list_from_string(nv_head, js_buf_p))
    {
        LOG_STD(APP_ERR, "Get list failed \n");
        return -1;
    }
     return 0;


}

/**
 * @brief 从链表中获取某个节点
 * @param nv_head 链表头
 * @param nv_name 节点名
 * @return 失败NULL，成功返回传入的obj地址，可根据节点类型取合适的类型值使用
 */
struct nv_obj *nv_get_list_obj(struct list_head *nv_head, const char *nv_name)
{
    struct list_head *pos;
    struct nv_list *obj_tmp;

    list_for_each(pos, nv_head)
    {
        obj_tmp = list_entry(pos, struct nv_list, list);
        if(strcmp(nv_name, obj_tmp->obj.nv_name) == 0)
        {
            return &obj_tmp->obj;
        }
    }

    return NULL;
}

/**
 * @brief 链表空间释放
 * @param 链表头
 */
void nv_list_free(struct list_head *nv_head)
{
    struct list_head *pos, *n;
    struct nv_list *obj_tmp;

    list_for_each_safe(pos, n, nv_head)
    {
        obj_tmp = list_entry(pos, struct nv_list, list);
        if(NULL != obj_tmp->obj.valuestring)
        {
            free(obj_tmp->obj.valuestring);
        }
        list_del(&obj_tmp->list);
        free(obj_tmp);
        obj_tmp = NULL;
    }
}

/**********************************************************************************/

static int add2list(cJSON *item, struct list_head *nv_head)
{
    struct nv_list *obj = (struct nv_list *)malloc(sizeof(struct nv_list));
    if(NULL == obj)
    {
        return -1;
    }

    //存储节点名称
    strncpy(obj->obj.nv_name, total_key, MAX_NV_NAME_LEN);
    //不管什么类型，valuestring都存放结果字符串
    char *ptmp = cJSON_Print(item);
    int str_len = strlen(ptmp) + 1;
    obj->obj.valuestring = (char *)malloc(str_len);
    if(NULL == obj->obj.valuestring)
    {
        LOG_STD(APP_ERR, "malloc err");
        free(obj);
        return -1;
    }
    memset(obj->obj.valuestring, 0, str_len);
    strcpy(obj->obj.valuestring, ptmp);
    free(ptmp);

    //存储节点值
    obj->obj.type = item->type;
    switch (item->type) {
    case cJSON_False:
    case cJSON_True:
    case cJSON_NULL:
        break;
    case cJSON_Number:
        obj->obj.valueint = item->valueint;
        obj->obj.valuedouble = item->valuedouble;
        break;
    case cJSON_String:
        strcpy(obj->obj.valuestring, item->valuestring);
        break;
    default:
        LOG_STD(APP_ERR, "err type");
        free(obj->obj.valuestring);
        free(obj);
        return -1;
        break;
    }

    //把节点加入链表中
    list_add_tail(&obj->list, nv_head);

    memset(total_key, 0, MAX_NV_NAME_LEN);

    return 0;
}

/**
 * @brief 把json数组的长度作为单独一个节点存储，比如plcnet.cloud是个json数组，那么会产生名为plcnet.cloud.arr_size节点
 * @param item
 * @param nv_head
 * @return 0 -1
 */
static int add_arr_size(cJSON *item, struct list_head *nv_head)
{
    if(cJSON_Array != item->type)
    {
        LOG_STD(APP_ERR, "not an array obj");
        return -1;
    }
    struct nv_list *obj = (struct nv_list *)malloc(sizeof(struct nv_list));
    if(NULL == obj)
    {
        return -1;
    }

    char nv_name[MAX_NV_NAME_LEN] = {0};

    for (int i=0; i<key_index; i++)
    {
        sprintf(nv_name, "%s%s.", nv_name, key_arr[i]);
    }

    sprintf(nv_name, "%s%s.arr_size", nv_name, item->string);
//    LOG_STD(APP_WARNING, "nv_name = %s", nv_name);

    //存储节点名称
    strncpy(obj->obj.nv_name, nv_name, MAX_NV_NAME_LEN);
    obj->obj.type = NV_Number;
    obj->obj.valueint = cJSON_GetArraySize(item);
    obj->obj.valuestring = (char *)malloc(10);
    if(NULL == obj->obj.valuestring)
    {
        LOG_STD(APP_ERR, "malloc err");
        free(obj);
        return -1;
    }
    memset(obj->obj.valuestring, 0, 10);
    snprintf(obj->obj.valuestring, 10, "%d", obj->obj.valueint);

    //把节点加入链表中
    list_add_tail(&obj->list, nv_head);

    return 0;
}

static int parse_json(cJSON *root, struct list_head *nv_head)
{
    if(root != NULL && cJSON_GetArraySize(root) == 0)  //json数组最终项
    {
        int j;
        for(j = 0; j < key_index-1; j++)
        {
            sprintf(total_key, "%s%s.", total_key, key_arr[j]);
        }
        sprintf(total_key, "%s%s", total_key, key_arr[j]);
//        sprintf(total_key, "%s=%s", total_key, cJSON_Print(root));
//        LOG_STD(APP_NOTICE, "total_key = %s", total_key);
        if(add2list(root, nv_head) < 0)
        {
            return -1;
        }
    }
    for(int i=0; i<cJSON_GetArraySize(root); i++)   //遍历最外层json键值对
    {
        cJSON *item = cJSON_GetArrayItem(root, i);
        if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归调用
        {
            key_arr[key_index] = (char *)malloc(OBJ_NAME_SIZE);
            strcpy(key_arr[key_index++], item->string);
            parse_json(item, nv_head);
            key_index--;
            free(key_arr[key_index]);
        }
        else if (cJSON_Array == item->type)
        {
            add_arr_size(item, nv_head);
            int size = cJSON_GetArraySize(item);
            for (int k = 0; k < size; k++)
            {
                cJSON *arr_item = cJSON_GetArrayItem(item, k);
                key_arr[key_index] = (char *)malloc(OBJ_NAME_SIZE);
                strcpy(key_arr[key_index], item->string);
                sprintf(key_arr[key_index], "%s[%d]", key_arr[key_index], k);
                key_index++;
                parse_json(arr_item, nv_head);
                key_index--;
                free(key_arr[key_index]);
            }
        }
        else  //值不为json对象就加入到链表中
        {
            for(int j=0; j<key_index; j++){
                //printf("%s.", key_arr[j]);
                sprintf(total_key, "%s%s.", total_key, key_arr[j]);
            }
            sprintf(total_key, "%s%s", total_key, item->string);
            //LOG_STD(APP_DEBUG, "total_key = %s", total_key);

            //到此total_key中存储是整个节点索引，分配节点存储
            if(add2list(item, nv_head) < 0)
            {
                return -1;
            }
        }
    }

    return 0;
}
